package knowledgebase.controller;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.util.*;
import javax.servlet.http.HttpServletResponse;
import knowledgebase.model.*;
import knowledgebase.service.DocumentService;
import knowledgebase.service.PermissionService;
import knowledgebase.service.RoleService;
import knowledgebase.service.UserService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

/**
 *
 * @author eugene
 */
@Controller
public class UserAndRoleController {

    Log log = LogFactory.getLog(getClass());
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private DocumentService documentService;
    @Autowired
    private PermissionService permissionService;

    /**
     * Returns {@link ModelAndView} for admin page
     * @return {@link ModelAndView} for admin page
     */
    @RequestMapping("/manage_users_and_roles")
    public ModelAndView manageUsersAndRoles() {
        ModelAndView model = new ModelAndView("admin_panel");
        model.addObject("isManagePermissions", false);
        model.addObject("availableRoles", roleService.getAllRoles());
        return model;
    }
    
    /**
     * Returns {@link ModelAndView} for admin page
     * @return {@link ModelAndView} for admin page
     */
    @RequestMapping("/manage_permissions")
    public ModelAndView managePermissions() {
        ModelAndView model = new ModelAndView("admin_panel");
        model.addObject("isManagePermissions", true);
        model.addObject("users", userService.getAllUser());
        model.addObject("availableRoles", roleService.getAllRoles());
        List<Document> documents = documentService.getAllDocuments();
        Map<Document, List<String>> documentMap = new TreeMap<Document, List<String>>();
        for(Document doc : documents) {
            documentMap.put(doc, Arrays.asList(doc.getHierarchy().split("<newlineseparartor>")));
        }
        model.addObject("documents", documentMap);
        model.addObject("user", new User());
        return model;
    }
    
    @RequestMapping(value = "/getUserCount", method = RequestMethod.GET)
    public @ResponseBody String getUserCount() {
        try {
            return userService.getUserCount()+"";
        } catch (Exception ex) {
            return ex.getStackTrace().toString();
        }
    }
    
    /**
     * Returns list of users
     * @param name
     * @param email
     * @param enabled
     * @param startIndex
     * @param size
     * @param response
     * @throws IOException 
     */
    @RequestMapping(value = "/searchUsers", method = RequestMethod.GET)
    @ResponseBody
    public void searchUsers(@RequestParam String name, @RequestParam String email, 
                            @RequestParam String enabled, @RequestParam int startIndex, 
                            @RequestParam int size, HttpServletResponse response) throws IOException {
        log.debug("searchUsers("+name+", "+email+", "+enabled+", "+startIndex+", "+size+")");
        
        MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
        Boolean isEnabled = enabled.isEmpty() ? null : new Boolean(enabled);
        List<User> list = userService.searchUser(name, email, isEnabled, startIndex, size);
        MediaType jsonMimeType = MediaType.APPLICATION_JSON;
        if (jsonConverter.canWrite(list.getClass(), jsonMimeType)) {
            jsonConverter.write(list, jsonMimeType, new ServletServerHttpResponse(response));
        }
    }
    
    @RequestMapping(value = "/getRoleCount", method = RequestMethod.GET)
    public @ResponseBody String getRoleCount() {
        try {
            return roleService.getUserCount()+"";
        } catch (Exception ex) {
            return ex.getStackTrace().toString();
        }
    }
    
    /**
     * Returns list of roles
     * @param name
     * @param startIndex
     * @param response
     * @param size
     * @throws IOException 
     */
    @RequestMapping(value = "/searchRoles", method = RequestMethod.GET)
    @ResponseBody
    public void searchRoles(@RequestParam String name, @RequestParam int startIndex, 
                            @RequestParam int size, HttpServletResponse response) throws IOException {
        log.debug("searchRoles("+name+", "+startIndex+", "+size+")");
        
        MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
        List<Role> list = roleService.searchRoles(name, startIndex, size);
        MediaType jsonMimeType = MediaType.APPLICATION_JSON;
        if (jsonConverter.canWrite(list.getClass(), jsonMimeType)) {
            jsonConverter.write(list, jsonMimeType, new ServletServerHttpResponse(response));
        }
    }
    
    @RequestMapping(value = "/get_global_role_permissions", method = RequestMethod.GET)
    @ResponseBody
    public void searchRolesGlobalPermissions(HttpServletResponse response) throws IOException {
        List<RoleGlobalPermission> rolesGlobalPermissions = permissionService.getAllRowGlobalPermissions();
        MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
        MediaType jsonMimeType = MediaType.APPLICATION_JSON;
        if (jsonConverter.canWrite(rolesGlobalPermissions.getClass(), jsonMimeType)) {
            jsonConverter.write(rolesGlobalPermissions, jsonMimeType, new ServletServerHttpResponse(response));
        }
    }
    
    @RequestMapping(value = "/set_global_role_permissions", method=RequestMethod.POST)
    public @ResponseBody String setGlobalPermissions(@RequestParam String globalRights) throws IOException {
        try{
            // prepares all variables
            List<SimpleGlobalPermission> globalPermissionsList = new ArrayList<SimpleGlobalPermission>();
            Gson gson = new Gson();
            JsonParser jsonParser = new JsonParser();
            
            JsonElement jsonElement = jsonParser.parse(globalRights);

            if(jsonElement.isJsonArray()){
                JsonArray jsonArray = jsonElement.getAsJsonArray();
                for( JsonElement enty : jsonArray){
                    SimpleGlobalPermission sgp = (SimpleGlobalPermission) gson.fromJson(enty, new SimpleGlobalPermission().getClass());
                    globalPermissionsList.add(sgp);
                }
                permissionService.setGlobalPermissionsForRoles(globalPermissionsList);
                return "ok";
            } else {
                return "Bad request.";
            }
        } catch (Exception ex){
            return ex.getMessage();
        }
    }



    /**
     * Register new user
     * @param name user login
     * @param password user password
     * @param roles user roles
     * @param email user email
     * @return 'ok' if user was added or error message if the exception was thrown
     */
    @RequestMapping(value = "/add_user", method = RequestMethod.GET)
    @ResponseBody
    public String addUser(@RequestParam(required = true, value = "name") String name, @RequestParam(required = true, value = "password") String password, @RequestParam(required = false, value = "roles") String roles, @RequestParam(required = true, value = "email") String email) {
        Set<Role> rolesSet = new HashSet<Role>();
        if (roles != null && !roles.trim().equals("")) {
            List<Role> allRoles = roleService.getAllRoles();
            for (String role : roles.split(",")) {
                Role userRole = new Role();
                try {
                    Long id = Long.valueOf(role);
                    userRole.setId(id);
                    rolesSet.add(allRoles.get(allRoles.indexOf(userRole)));
                } catch (NumberFormatException ex) {
                }
            }
        }
        try {
            userService.saveOrUpdateUser(new User(name, password, email, rolesSet, true, new Date()));
            return "ok";
        } catch (Exception ex) {
            return ex.getMessage();
        }
    }

    /**
     * Returns user by name
     * @param name user name
     * @param response
     * @throws IOException 
     */
    @RequestMapping(value = "/get_user_by_name", method = RequestMethod.GET)
    @ResponseBody
    public void getUserByName(@RequestParam String name, HttpServletResponse response) throws IOException {
        MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
        User user = userService.findUserByName(name);
        MediaType jsonMimeType = MediaType.APPLICATION_JSON;
        if (jsonConverter.canWrite(user.getClass(), jsonMimeType)) {
            jsonConverter.write(user, jsonMimeType, new ServletServerHttpResponse(response));
        }
    }

    /**
     * Remove user by id
     * @param userId user id
     * @return  'ok' if user was added or error message if the exception was thrown
     */
    @RequestMapping(value = "/remove_user/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public String removeUser(@PathVariable("userId") Long userId) {
        if(userId==1) return "You cannot delete Admin account.";
        
        try {
            userService.removeUserById(userId.longValue());
            return "ok";
        } catch (Exception ex) {
            return ex.getStackTrace().toString();
        }
    }

    @RequestMapping("/edit_user")
    @ResponseBody
    public String editUser(@RequestParam Long id, @RequestParam String login, @RequestParam String password, @RequestParam String email, @RequestParam Boolean isEnabled, @RequestParam String roles) {
        Set<Role> rolesSet = new HashSet<Role>();
        if (roles != null) {
            List<Role> allRoles = roleService.getAllRoles();
            for (String role : roles.split(",")) {
                Role userRole = new Role();
                try {
                    Long roleId = Long.valueOf(role);
                    userRole.setId(roleId);
                    rolesSet.add(allRoles.get(allRoles.indexOf(userRole)));
                } catch (NumberFormatException ex) {
                }
            }
        }
        User user = new User(id, login, password, email, rolesSet, isEnabled, new Date());
        try {
            userService.saveOrUpdateUser(user);
            return "ok";
        } catch (Exception ex) {
            log.error("", ex);
            return ex.getMessage();
        }
    }

    @RequestMapping("/add_role")
    @ResponseBody
    public String addRole(@RequestParam(required = true, value = "name") String name) {
        try {
            roleService.saveOrUpdateRole(new Role(name));
            return "ok";
        } catch (Exception ex) {
            return ex.getMessage();
        }
    }

    @RequestMapping("/remove_role/{roleId}")
    @ResponseBody
    public String removeRole(@PathVariable("roleId") Long roleId) {
        if(roleId==1) return "You cannot delete ROLE_ADMIN.";
        
        try {
            roleService.removeRole(roleId);
            return "ok";
        } catch (Exception ex) {
            return ex.getMessage();
        }
    }
    
    @RequestMapping(value = "/get_role_by_name", method = RequestMethod.GET)
    @ResponseBody
    public void getRoleByName(@RequestParam String name, HttpServletResponse response) throws IOException {
        MappingJacksonHttpMessageConverter jsonConverter = new MappingJacksonHttpMessageConverter();
        Role role = roleService.getRoleByName(name);
        MediaType jsonMimeType = MediaType.APPLICATION_JSON;
        if (jsonConverter.canWrite(role.getClass(), jsonMimeType)) {
            jsonConverter.write(role, jsonMimeType, new ServletServerHttpResponse(response));
        }
    }
    
    @RequestMapping(value = "/welcome/{name}", method = RequestMethod.GET)
    public ModelAndView welcome(@PathVariable String name) {
        ModelAndView model = new ModelAndView("welcome");
        model.addObject("userName", name);
        return model;
    }

    @RequestMapping("/registration")
    public ModelAndView getUserRegistrationView() {
        return new ModelAndView("registration");
    }
    
    @RequestMapping(value = "/set_permissions", method = RequestMethod.POST)
    @ResponseBody
    public String setPermissions(@RequestParam String[] ids, @RequestParam Boolean read, @RequestParam Boolean edit, @RequestParam Boolean remove, @RequestParam String[] userIds, @RequestParam String[] roleIds) {
        try{
            if(userIds[0].equalsIgnoreCase("null")) userIds=null;
            if(roleIds[0].equalsIgnoreCase("null")) roleIds=null;
            permissionService.setPermisiions(ids, read, edit, remove, userIds, roleIds);
            return "ok";
        } catch (Exception ex){
            return ex.getMessage();
        }
    }
    
    @RequestMapping(value = "/save_role_global_permissions")
    public void saveRoleGlobalPermissions(@RequestBody SimpleGlobalPermission[] permissopnsModel) {
        log.info("\n_________________\n"+permissopnsModel.length+"\n________");
    }
}
